/*
 * 功能需求：
 * 1. 检测按键的短按和长按事件。
 * 2. 短按：当按键被按下并释放时触发。
 * 3. 长按：当按键被按下超过500毫秒时立即触发，并且不会重复输出。
 *
 * 思路：
 * 1. 使用外部中断（CHANGE模式）检测按键的状态变化（按下或释放）。
 * 2. 使用 `Ticker` 库来处理定时器功能，检测按键是否被长按。
 * 3. 在按键按下时启动定时器，如果按键被按下超过设定的时间（如500毫秒），则视为长按并立即输出。
 * 4. 如果按键在长按时间之前释放，则视为短按并输出相应信息。
 * 5. 设置标志位防止多次触发长按事件。
 */

#include <Arduino.h>
#include <Ticker.h>

#define BTN_PIN 9            // 定义按键引脚
#define DEBOUNCE_DELAY 30    // 按键消抖延迟时间（毫秒）
#define LONG_PRESS_TIME 2000 // 长按判定时间为2000毫秒

Ticker longPressTicker;           // 定义一个 Ticker 对象用于定时器功能
bool isButtonPressed = false;     // 标记按键是否被按下
bool isLongPressDetected = false; // 标记是否检测到长按事件

// 长按事件处理函数
void handleLongPress()
{
    if (isButtonPressed && !isLongPressDetected)
    {
        Serial.println("Button long pressed!"); // 立即输出长按事件
        isLongPressDetected = true;             // 设置长按标志，防止重复触发
    }
}

// 中断服务程序，检测按键状态变化
void IRAM_ATTR checkSwitch()
{
    static unsigned long buttonLastMillis = 0; // 上一次检测的时间戳
    unsigned long newMillis = millis();        // 获取当前时间戳

    // 检查是否超过了消抖延迟时间
    if (newMillis - buttonLastMillis > DEBOUNCE_DELAY)
    {
        buttonLastMillis = newMillis; // 更新上一次检测的时间戳

        if (digitalRead(BTN_PIN) == LOW)
        { // 按键被按下
            if (!isButtonPressed)
            { // 如果按键之前没有被按下
                isButtonPressed = true;
                isLongPressDetected = false;                                       // 重置长按标志
                longPressTicker.attach(LONG_PRESS_TIME / 1000.0, handleLongPress); // 启动软件定时器，等待长按事件
            }
        }
        else
        { // 按键被释放
            if (isButtonPressed)
            {
                if (!isLongPressDetected)
                {
                    Serial.println("Button short pressed!"); // 输出短按事件
                }
                isButtonPressed = false;
                longPressTicker.detach(); // 停止软件定时器，防止误触发
            }
        }
    }
}

void setup()
{
    Serial.begin(115200);                                                 // 初始化串口通信
    pinMode(BTN_PIN, INPUT_PULLUP);                                       // 设置按键引脚为输入上拉模式
    attachInterrupt(digitalPinToInterrupt(BTN_PIN), checkSwitch, CHANGE); // 使用CHANGE模式以检测按键的上升沿和下降沿
}

void loop()
{
    // 如果不需要在主循环做任何事情，保持为空即可
}